#ifndef VALUE_H
#define VALUE_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>
#include <pthread.h>
#include <math.h>
#include <time.h>

typedef struct Constructor Constructor;
typedef struct Translator Translator;
typedef struct Scanner Scanner;
typedef struct Instruction Instruction;
typedef struct Syntax Syntax;
typedef struct Chunk Chunk;
typedef struct Token Token;

typedef struct TValue TValue;
typedef struct Prototype Prototype;
typedef struct Object Object;
typedef struct Exception Exception;
typedef struct Upvalue Upvalue;
typedef struct Closure Closure;
typedef struct String String;

typedef struct Line Line;

typedef __int8 _byte;
typedef __int16 _short;
typedef __int32 _int;
typedef __int64 _long;
typedef double _double;
typedef __int8 _boolean;

typedef unsigned __int8 u_byte;
typedef unsigned __int16 u_short;
typedef unsigned __int32 u_int;
typedef unsigned __int64 u_long;

typedef enum
{
	T_NULL,
	T_BOOLEAN,
	T_INTEGER,
	T_DECIMAL,
	T_C_POINTER,
	T_UPVALUE,
	T_STRING,
	T_MAP,
	T_QUEUE,
	T_CLOSURE,
	T_EXCEPTION,
} TVALUE_CATEGORY;

typedef enum
{
	NONE,
	BLACK,
	WHITE,
	GREY,
} COLOR;

typedef struct Object
{
	TVALUE_CATEGORY category;
	COLOR color;
	void (*delete)(Object *);
	struct Object *next;
} Object;

#define ObjectHeader          \
	TVALUE_CATEGORY type;     \
	COLOR color;              \
	void (*delete)(Object *); \
	struct Object *next;      \
	struct Line *line;

typedef struct TValue
{
	TVALUE_CATEGORY category;
	union
	{
		_int boolean;
		_long integer;
		double decimal;
		Upvalue *upvalue;
		void *pointer;
		Object *object;
	} value;
} TValue;

typedef _int (*C_Function)(Line *, Prototype *, _int);

#define TNULL \
	((TValue){T_NULL})
#define TBOOLEAN(value)                 \
	(TValue)                            \
	{                                   \
		T_BOOLEAN, { .boolean = value } \
	}
#define TINTEGER(value)                 \
	(TValue)                            \
	{                                   \
		T_INTEGER, { .integer = value } \
	}
#define TDECIMAL(value)                 \
	(TValue)                            \
	{                                   \
		T_DECIMAL, { .decimal = value } \
	}
#define TPOINTER(value)                   \
	(TValue)                              \
	{                                     \
		T_C_POINTER, { .pointer = value } \
	}
#define TSTRING(value)                            \
	(TValue)                                      \
	{                                             \
		T_STRING, { .object = (Object *)(value) } \
	}
#define TQUEUE(value)                            \
	(TValue)                                     \
	{                                            \
		T_QUEUE, { .object = (Object *)(value) } \
	}
#define TMAP(value)                            \
	(TValue)                                   \
	{                                          \
		T_MAP, { .object = (Object *)(value) } \
	}
#define TCLOSURE(value)                            \
	(TValue)                                       \
	{                                              \
		T_CLOSURE, { .object = (Object *)(value) } \
	}

#define IS_NULL(self) ((self).category == T_NULL)
#define IS_BOOLEAN(self) ((self).category == T_BOOLEAN)
#define IS_INTEGER(self) ((self).category == T_INTEGER)
#define IS_DECIMAL(self) ((self).category == T_DECIMAL)
#define IS_C_POINTER(self) ((self).category == T_C_POINTER)
#define IS_UPVALUE(self) ((self).category == T_UPVALUE)

#define IS_STRING(self) ((self).category == T_STRING)
#define IS_QUEUE(self) ((self).category == T_QUEUE)
#define IS_MAP(self) ((self).category == T_MAP)
#define IS_EXCEPTION(self) ((self).category == T_EXCEPTION)
#define IS_CLOSURE(self) ((self).category == T_CLOSURE)
#define IS_OBJECT(self) ((self).category >= T_STRING)

#define TO_BOOLEAN(self) ((self).value.boolean)
#define TO_INTEGER(self) ((self).value.integer)
#define TO_DECIMAL(self) ((self).value.decimal)
#define TO_C_POINTER(self) ((self).value.pointer)
#define TO_UPVALUE(self) ((self).value.upvalue)

#define TO_STRING(self) (String *)((self).value.object)
#define TO_QUEUE(self) (Queue *)((self).value.object)
#define TO_MAP(self) (Map *)((self).value.object)
#define TO_CLOSURE(self) (Function *)((self).value.object)
#define TO_EXCEPTION(self) (Exception *)((self).value.object)

typedef struct Array
{
	void *pointer;
	u_int length;
	u_int size;
} Array;

#define Array_Init(self, type) \
	*(self) = (Array) { Globe_Malloc(sizeof(type) * 8), 0, 8 }

#define Array_Push(self, value, type)                                                  \
	if ((self)->length == (self)->size)                                                \
	{                                                                                  \
		(self)->size <<= 1;                                                            \
		(self)->pointer = Globe_Realloc((self)->pointer, sizeof(type) * (self)->size); \
	}                                                                                  \
	((type *)(self)->pointer)[(self)->length] = (value);                               \
	(self)->length++

#define Array_Pop(self, type)                  \
	((type *)(self)->pointer)[(self)->length]; \
	(self)->length--

#define Array_Peek(self, type) \
	((type *)(self)->pointer)[(self)->length]

#define Array_Get(self, index, type) ((type *)(self)->pointer)[(index)]
#define Array_Set(self, index, value, type) ((type *)(self)->pointer)[(index)] = (value);
#define Array_Clear(self) (self)->length = 0

#define Array_Free(self) Globe_Free((self)->pointer);

typedef enum
{
	TOKEN_ID,
	TOKEN_STRING,
	TOKEN_DECIMAL,
	TOKEN_INTEGER,
	TOKEN_PLUS,
	TOKEN_MINUS,
	TOKEN_MUL,
	TOKEN_DIV,
	TOKEN_MOD,
	TOKEN_POS,
	TOKEN_NEG,
	TOKEN_POWER,
	TOKEN_LEFTPARENTHESIS,
	TOKEN_RIGHTPARENTHESIS,
	TOKEN_LEFTBRACKET,
	TOKEN_RIGHTBRACKET,
	TOKEN_LEFTBRACE,
	TOKEN_RIGHTBRACE,
	TOKEN_ASSIGN,
	TOKEN_SEMICOLON,
	TOKEN_COMMA,
	TOKEN_POINT,
	TOKEN_CONCAT,
	TOKEN_EQUAL,
	TOKEN_NOTEQUAL,
	TOKEN_LESS,
	TOKEN_LESSEQUAL,
	TOKEN_GREATER,
	TOKEN_GREATEREQUAL,
	TOKEN_MOVELEFTBIT,
	TOKEN_MOVERIGHTBIT,
	TOKEN_ANDBIT,
	TOKEN_ORBIT,
	TOKEN_NOTBIT,
	TOKEN_COLON,
	TOKEN_EOF,

	TOKEN_AND,
	TOKEN_FALSE,
	TOKEN_RETURN,
	TOKEN_BREAK,
	TOKEN_CONTINUE,
	TOKEN_IF,
	TOKEN_LET,
	TOKEN_NIL,
	TOKEN_NOT,
	TOKEN_OR,
	TOKEN_TRUE,
	TOKEN_FOR,
	TOKEN_IN,
	TOKEN_FUNCTION,
	TOKEN_ELSEIF,
	TOKEN_ELSE,
	TOKEN_IMPORT,
	TOKEN_FROM,
	TOKEN_PACKAGE,
	TOKEN_INTEGER_TYPE,
	TOKEN_DECIMAL_TYPE,
	TOKEN_BOOLEAN_TYPE,
	TOKEN_STRING_TYPE,
	TOKEN_QUEUE_TYPE,
	TOKEN_MAP_TYPE,
	TOKEN_OBJECT_TYPE,
	TOKEN_TRY,
	TOKEN_CATCH,
} TOKEN_CATEGORY;

static char *TOKEN_CATEGORY_STRING[] = {
	"TOKEN_ID",
	"TOKEN_STRING",
	"TOKEN_DECIMAL",
	"TOKEN_INTEGER",
	"TOKEN_PLUS",
	"TOKEN_MINUS",
	"TOKEN_MUL",
	"TOKEN_DIV",
	"TOKEN_MOD",
	"TOKEN_POS",
	"TOKEN_NEG",
	"TOKEN_POWER",
	"TOKEN_LEFTPARENTHESIS",
	"TOKEN_RIGHTPARENTHESIS",
	"TOKEN_LEFTBRACKET",
	"TOKEN_RIGHTBRACKET",
	"TOKEN_LEFTBRACE",
	"TOKEN_RIGHTBRACE",
	"TOKEN_ASSIGN",
	"TOKEN_SEMICOLON",
	"TOKEN_COMMA",
	"TOKEN_POINT",
	"TOKEN_CONCAT",
	"TOKEN_EQUAL",
	"TOKEN_NOTEQUAL",
	"TOKEN_LESS",
	"TOKEN_LESSEQUAL",
	"TOKEN_GREATER",
	"TOKEN_GREATEREQUAL",
	"TOKEN_MOVELEFTBIT",
	"TOKEN_MOVERIGHTBIT",
	"TOKEN_ANDBIT",
	"TOKEN_ORBIT",
	"TOKEN_NOTBIT",
	"TOKEN_COLON",
	"TOKEN_EOF",

	"TOKEN_AND",
	"TOKEN_FALSE",
	"TOKEN_RETURN",
	"TOKEN_BREAK",
	"TOKEN_CONTINUE",
	"TOKEN_IF",
	"TOKEN_LET",
	"TOKEN_NIL",
	"TOKEN_NOT",
	"TOKEN_OR",
	"TOKEN_TRUE",
	"TOKEN_FOR",
	"TOKEN_IN",
	"TOKEN_FUNCTION",
	"TOKEN_ELSEIF",
	"TOKEN_ELSE",
	"TOKEN_IMPORT",
	"TOKEN_FROM",
	"TOKEN_PACKAGE",
	"TOKEN_INTEGER_TYPE",
	"TOKEN_DECIMAL_TYPE",
	"TOKEN_BOOLEAN_TYPE",
	"TOKEN_STRING_TYPE",
	"TOKEN_QUEUE_TYPE",
	"TOKEN_MAP_TYPE",
	"TOKEN_OBJECT_TYPE",
	"TOKEN_TRY",
	"TOKEN_CATCH",
};

typedef struct Token
{
	TOKEN_CATEGORY category;
	u_int line;
	TValue value;
} Token;

void TValue_Print(const TValue *value);
// String* TValue_ToString(const TValue *value);
bool TValue_Equals(const TValue *v1, const TValue *v2);
#endif